/*
* Copyright (C) 2004-2007  Autodesk, Inc.
* 
* This library is free software; you can redistribute it and/or
* modify it under the terms of version 2.1 of the GNU Lesser
* General Public License as published by the Free Software Foundation.
* 
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
* 
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*
*/

#include "stdafx.h"
#include "FdoWcsSelectCommand.h"
#include "FdoWcsFeatureReader.h"
#include "FdoWcsUtils.h"
#include "FdoWcsGlobals.h"
#include "FdoWcsXmlGlobals.h"
#include "FdoWcsCoverageSummary.h"
#include "FdoWcsCoverageSummaryCollection.h"
#include "FdoWcsRequestMetadata.h"
#include <WCS/Override/FdoWcsOvPhysicalSchemaMapping.h>
#include "FdoWcsRasterGdal.h"

#include <malloc.h>
#include <math.h>

#define MAXPOWER2RESOLUTION 4096
#define DEFAULTRESOLUTIONX 128
#define DEFAULTRESOLUTIONY 128

FdoWcsSelectCommand::FdoWcsSelectCommand (FdoWcsConnection* connection) :
FdoWcsFeatureCommand<FdoISelect> (connection),
mType (FdoLockType_None),
mStrategy (FdoLockStrategy_All)
{
    mPropertiesToSelect = FdoIdentifierCollection::Create ();
}

/** Do not implement the copy constructor. **/
//FdoWcsSelectCommand::FdoWcsSelectCommand (const FdoWcsSelectCommand &right) { }

FdoWcsSelectCommand::~FdoWcsSelectCommand (void)
{
}

// <summary>Dispose this object.</summary>
// <returns>Returns nothing.</returns> 
void FdoWcsSelectCommand::Dispose ()
{
    delete this;
}

/// <summary>Gets the FdoIdentifierCollection that holds the list of property names to 
/// return with the result. If empty all properties of the specified class
/// are returned.</summary>
/// <returns>Returns the list of property names.</returns> 
FdoIdentifierCollection* FdoWcsSelectCommand::GetPropertyNames ()
{
    if (mPropertiesToSelect == NULL)
        mPropertiesToSelect = FdoIdentifierCollection::Create();

    return (FDO_SAFE_ADDREF(mPropertiesToSelect.p));
}

/// <summary>Gets the FdoLockType value (see "Locking Commands").</summary>
/// <returns>Returns the lock type.</returns> 
FdoLockType FdoWcsSelectCommand::GetLockType ()
{
    return (mType);
}

/// <summary>Sets the FdoLockType value (see "Locking Commands").</summary>
/// <param name="value">Input the lock type.</param> 
/// <returns>Returns nothing</returns> 
void FdoWcsSelectCommand::SetLockType (FdoLockType value)
{
    throw FdoSchemaException::Create(NlsMsgGet(FDOWCS_LOCKING_NOT_SUPPORTED, "Locking not supported."));
}

/// <summary>Gets the FdoLockStrategy value (see "Locking Commands").</summary>
/// <returns>Returns the lock strategy.</returns> 
FdoLockStrategy FdoWcsSelectCommand::GetLockStrategy ()
{
    return (mStrategy);
}

/// <summary>Sets the FdoLockStrategy value (see "Locking Commands").</summary>
/// <param name="value">Input the lock strategy.</param> 
/// <returns>Returns nothing</returns> 
void FdoWcsSelectCommand::SetLockStrategy (FdoLockStrategy value)
{
    throw FdoSchemaException::Create(NlsMsgGet(FDOWCS_LOCKING_NOT_SUPPORTED, "Locking not supported."));
}


/// <summary>Executes the select command and returns a reference to an FdoIFeatureReader.</summary>
/// <returns>Returns the feature reader.</returns> 
FdoIFeatureReader* FdoWcsSelectCommand::Execute ()
{
    VALIDATE_ARGUMENT (mClassName);

    FdoWcsDelegateP wcsDelegate = mConnection->GetWcsDelegate();

    FdoStringP schemaName = mClassName->GetSchemaName ();
    FdoStringP clsName = mClassName->GetName ();	
    VALIDATE_ARGUMENT (clsName);

    // Get the class in the schemas
    FdoFeatureClassP featClass;
    FdoFeatureSchemasP schemas = mConnection->GetSchemas ();
    if (schemaName != NULL && schemaName.GetLength() > 0)
    {
        FdoFeatureSchemaP schema = schemas->GetItem (schemaName);
        if (schema == NULL)
            throw FdoSchemaException::Create (NlsMsgGet (FDOWCS_NAMED_SCHEMA_NOT_FOUND, "FDO Schema '%1$ls' was not found.", (FdoString*)schemaName));

        FdoPtr<FdoClassCollection> featClasses = schema->GetClasses ();
        featClass = static_cast<FdoFeatureClass *> (featClasses->GetItem (clsName));
    }
    else
    {
        FdoPtr<FdoIDisposableCollection> featClasses = schemas->FindClass (clsName);	
        if (featClasses->GetCount () == 0)
            throw FdoSchemaException::Create (NlsMsgGet(FDOWCS_NAMED_SCHEMACLASS_NOT_FOUND, "FDO Feature Class '%1$ls' was not found.", (FdoString*)clsName ));

        if (featClasses->GetCount () > 1)
            throw FdoSchemaException::Create (NlsMsgGet(FDOWCS_NAMED_SCHEMACLASS_NOT_FOUND, "Duplicate FDO Feature Class '%1$ls' found in Schema.", (FdoString*)clsName ));

        featClass = static_cast<FdoFeatureClass *> (featClasses->GetItem (0));
    }

    if (featClass == NULL)
        throw FdoSchemaException::Create (NlsMsgGet(FDOWCS_NAMED_SCHEMACLASS_NOT_FOUND, "FDO Feature Class '%1$ls' was not found.", (FdoString*)clsName));

    // Check whether the feat class is abstract or not.
    if (featClass->GetIsAbstract ())
        throw FdoSchemaException::Create (NlsMsgGet(FDOWCS_CANNOT_QUERY_ABSTRACT_CLASS, "FDO Query cann't be performed on abstract class '%1$ls'.", (FdoString*)clsName ));


    FdoStringP coverageName;
    FdoStringP spatialContextName;
    FdoStringP imageFormat;

    // Retrieve the coverage name that corresponds to the FDO clas name
    if (!mConnection->IsConfigured ())
    {
        FdoDictionaryP namedLayerMappings = mConnection->GetNamedCoverageMappings();
        FdoDictionaryElementP layerElement = namedLayerMappings->GetItem(clsName);
        if (layerElement)
            coverageName = layerElement->GetValue();
        else
            throw FdoSchemaException::Create (NlsMsgGet (FDOWCS_12001_COVERAGE_NOT_EXIST, "The WCS coverage '%1$ls' does not exist.", (FdoString*)clsName));
    }
    else
    {
        FdoSchemaMappingsP schemaMappings = mConnection->GetSchemaMappings ();
        FdoInt32 cntMappings = schemaMappings->GetCount ();
        for (FdoInt32 i=0; i<cntMappings; i++)
        {
            FdoPtr<FdoPhysicalSchemaMapping> schemaMapping = schemaMappings->GetItem (i);
            if (schemaName.GetLength () > 0)
            {
                if (wcscmp (schemaMapping->GetName (), schemaName) != 0)
                    continue;
            }

            FdoWcsOvPhysicalSchemaMapping* physicalMapping = static_cast<FdoWcsOvPhysicalSchemaMapping *> (schemaMapping.p);
            if (physicalMapping == NULL)
                throw FdoSchemaException::Create (NlsMsgGet (FDOWCS_SCHEMA_MAPPING_NOT_FOUND, "Physical schema mapping '%1$ls' was not found.", (FdoString*)schemaName));

            FdoWcsOvClassesP classMappings = physicalMapping->GetClasses ();
            FdoWcsOvClassDefinitionP clsDefinition = classMappings->GetItem (clsName);

            if (clsDefinition != NULL)
            {
                // Now we've got the feature class. Then we can get the coverage name.
                FdoWcsOvRasterDefinitionP rasterDefinition = clsDefinition->GetRasterDefinition ();
                FdoWcsOvCoveragesP coverages = rasterDefinition->GetCoverages();
                for (FdoInt32 k=0; k<coverages->GetCount (); k++)
                {
                    FdoWcsOvCoverageDefinitionP coverage = coverages->GetItem (k);
                    FdoStringP coverageName = coverage->GetName ();
                    if (coverageName != NULL && coverageName.GetLength () > 0)
                    {
                        FdoStringP originalName = _getOriginalCoverageIdentifier (coverageName);
                        // if the coverage is not found, the name used here can be
                        // qualified name of the feature class, such as "WCS_Schema:SomeCoverage"
                        // or "WCS_Schema:Some Coverage".
                        if (originalName.GetLength() == 0)
                        {
                            FdoStringP tempCoverageName = coverageName.Right (L":");
                            originalName = _getOriginalCoverageIdentifier (tempCoverageName);
                        }

                        if (originalName.GetLength () > 0)
                            coverageName =  originalName;
                        else
                        {
                            // The coverage name must be wrong.
                            throw FdoCommandException::Create (
                                NlsMsgGet(FDOWCS_12001_COVERAGE_NOT_EXIST, "The WCS coverage '%1$ls' does not exist.", (FdoString *)coverageName));
                        }
                    }
                    else
                        throw FdoSchemaException::Create (NlsMsgGet (FDOWCS_PHYSICAL_SCHEMA_LAYER_HAS_NO_NAME, 
                        "A WCS coverage has no name in physical schema mappings."));

                }

                if (coverageName.GetLength() <= 0)
                    throw FdoSchemaException::Create (NlsMsgGet(FDOWCS_PHYSICAL_CLASS_DEFINITION_HAS_NO_LAYERS, 
                    "No WCS coverage are found in physical class definition '%1$ls'.",
                    (FdoString*)clsName));

                // Get the spatial context used for the layers
                spatialContextName = rasterDefinition->GetSpatialContextName ();

                FdoWcsOvCoveragesP coverageDefinitions = rasterDefinition->GetCoverages();

                //only handle first coverage now according WCS spec
                if (coverageDefinitions->GetCount()>0)
                {
                    FdoWcsOvCoverageDefinitionP coverageDefinition = coverageDefinitions->GetItem(0);
                    
                    //Get the preferred image format from the overrides
                    imageFormat = coverageDefinition->GetImageFormat();

                    //TODO: Get more override values here!
                }
            }			
        }		
    }

    // Check whether the spatial context is supported by the coverage.
    // If the user has not set the spatial context by calling ActivateSpatialContext command,
    // the active spatial context should be NULL. In this case, the first spatial context in
    // the list of the spatial contexts associated to the WCS coverage will be used as default.
    FdoStringP srsName = mConnection->GetActiveSpatialContext ();
    FdoPtr<FdoOwsBoundingBox> bbox = FdoOwsBoundingBox::Create ();

    // Get the CoverageSummary object
    FdoWcsServiceMetadataP metadata = mConnection->GetWcsServiceMetadata ();
    FdoPtr<FdoWcsCapabilities> capa = static_cast<FdoWcsCapabilities *> (metadata->GetCapabilities ());
    FdoWcsCoverageSummaryCollectionP coverageSummaries = capa->GetCoverageSummaries();
    FdoWcsCoverageSummaryP coverageSummary;
    if (coverageSummaries->GetCount () > 0)
    {
        coverageSummary = mConnection->FindCoverageSummary(coverageSummaries,coverageName);
        if (coverageSummary == NULL)
            throw FdoSchemaException::Create (NlsMsgGet (FDOWCS_12001_COVERAGE_NOT_EXIST, "The WCS coverage '%1$ls' does not exist.", (FdoString*)coverageName));
    }

    FdoSize height = 0;
    FdoSize width = 0; 

    if (spatialContextName.GetLength () > 0)
        srsName = spatialContextName;

    if (srsName != NULL)
    {
        bool crsSupported = coverageSummary->FindCoordinateReferenceSystem (srsName);
		// TODO: if the CRS is not found in the coverage summary, we should try to retrieve
		// it from the relevant coverage description. (Call mConnection->GetCRSesFromDescription).
        if (crsSupported)
        {
            // associate the spatial context to the raster property of the class
            _setSpatialContextAssociation (featClass, srsName);
            bbox->SetCRS (srsName);
        }
        else 
        {
            throw FdoException::Create (
                NlsMsgGet (FDOWCS_12003_SPATIALCONTEXT_NOT_SUPPORTED_BY_LAYER, 
                "The spatial context '%1$ls' is not supported by the coverage '%2$ls'.",
                (FdoString*)srsName,
                (FdoString*)coverageName));
        }
    }
    else
    {
        // get the default spatial context associated with the class
        srsName = _getSpatialContextAssociation (featClass);
        bbox->SetCRS (srsName);
    }

    // Calculate the bounding box of the coverage
    _calcCoverageBoundingBox(coverageSummary,srsName,bbox);

    // Get the image format
    if (!mConnection->IsConfigured ())
    {
        imageFormat = mConnection->GetDefaultImageFormat (featClass->GetName());
    }

    // Analysis the properties
    FdoStringP computedProperty;
    _analysisIdentifier (srsName, bbox, computedProperty, height, width);

    // Get the selected properties
    FdoPtr<FdoClassDefinition> classDefPruned = _getPrunedClassDefinition ();

    // Get the WCS version
    FdoStringP wcsVersion = metadata->GetVersion ();

    // Retrieve the raster stream through the WCS GetCoverage Request
    FdoPtr<FdoIoStream> stream = wcsDelegate->GetCoverage(coverageName, height, width,bbox, imageFormat, false, NULL, NULL, wcsVersion);

    // Create a FeatureReader on the stream and return it to the user
    FdoPtr<FdoWcsFeatureReader> ret;
    FdoWcsRect rect(bbox->GetMinX(), bbox->GetMinY(), bbox->GetMaxX(), bbox->GetMaxY());
    ret = new FdoWcsFeatureReader (stream, rect, featClass, classDefPruned);

    //FdoWcsRasterGdal* p = new FdoWcsRasterGdal(stream);
    //p->_getDataset();
    //p->_getRasterBands();

    return (FDO_SAFE_ADDREF (ret.p));
}

/// <summary>Executes the select command and returns a reference to an
/// IFeatureReader.</summary> 
/// <returns>Returns the feature reader.</returns> 
FdoIFeatureReader* FdoWcsSelectCommand::ExecuteWithLock ()
{
    FdoPtr<FdoWcsFeatureReader> ret;

    ret = new FdoWcsFeatureReader ();

    return (FDO_SAFE_ADDREF (ret.p));
}

/// <summary> When executing the operation ExecuteWithLock lock 
/// conflicts may occur. Those lock conflicts are reported. The 
/// function GetLockConflicts returns a lock conflict reader 
/// providing access to the list of lock conflicts that occurred 
/// during the execution of the operation.</summary>
/// <returns>Returns a lock conflict reader.</returns> 
FdoILockConflictReader* FdoWcsSelectCommand::GetLockConflicts ()
{
    throw FdoCommandException::Create (NlsMsgGet (FDOWCS_LOCKING_NOT_SUPPORTED, "Locking not supported."));
}

FdoClassDefinition* FdoWcsSelectCommand::_getPrunedClassDefinition ()
{
    // Get the original class definition
    FdoPtr<FdoFeatureClass> clsDef;
    FdoStringP clsName;
    FdoStringP schemaName;

    FdoPtr<FdoIdentifier> identity = this->GetFeatureClassName ();
    clsName = identity->GetName ();
    schemaName = identity->GetSchemaName ();

    FdoFeatureSchemasP schemas = mConnection->GetSchemas ();

    if (schemaName == L"")
    {		
        FdoPtr<FdoIDisposableCollection> clsColl = schemas->FindClass (clsName);
        if (clsColl->GetCount () == 0)
            throw FdoSchemaException::Create (NlsMsgGet(FDOWCS_NAMED_SCHEMACLASS_NOT_FOUND, "FDO Feature Class '%1$ls' was not found.", (FdoString*)clsName ));

        clsDef = dynamic_cast<FdoFeatureClass *> (clsColl->GetItem(0));
    }
    else
    {
        FdoPtr<FdoFeatureSchema> schema = schemas->FindItem (schemaName);
        if (schema == NULL)
            throw FdoSchemaException::Create (NlsMsgGet(FDOWCS_NAMED_SCHEMA_NOT_FOUND, "FDO Schema '%1$ls' was not found.", (FdoString*)schemaName));

        FdoPtr<FdoClassCollection> clsColl = schema->GetClasses ();
        clsDef = dynamic_cast<FdoFeatureClass *> (clsColl->FindItem (clsName));
    }

    FdoPtr<FdoClassDefinition> ret = FdoCommonSchemaUtil::DeepCopyFdoFeatureClass (clsDef);
    if (mPropertiesToSelect && mPropertiesToSelect->GetCount () == 0)
    {
        return FDO_SAFE_ADDREF (ret.p);
    }

    FdoPtr<FdoPropertyDefinition> prop;
    FdoPtr<FdoPropertyDefinitionCollection> properties = ret->GetProperties ();
    FdoPtr<FdoDataPropertyDefinitionCollection> ids = ret->GetIdentityProperties();

    // find the raster property first in the properties
    FdoPtr<FdoRasterPropertyDefinition> rasterProp;
    bool bRaster = false;
    for (FdoInt32 i=0; i<properties->GetCount(); i++)
    {
        FdoPtr<FdoPropertyDefinition> prop = properties->GetItem (i);
        if (prop->GetPropertyType () == FdoPropertyType_RasterProperty)
        {
            // we found that raster property
            rasterProp = static_cast<FdoRasterPropertyDefinition *> (FDO_SAFE_ADDREF (prop.p));
            bRaster = true;
            break;
        }
    }

    // if the raster property is not in properties, search it in the base properties 
    if (!bRaster)
    {
        FdoPtr<FdoReadOnlyPropertyDefinitionCollection> baseProps = ret->GetBaseProperties ();
        for (FdoInt32 i=0; i<baseProps->GetCount(); i++)
        {
            FdoPtr<FdoPropertyDefinition> prop = baseProps->GetItem (i);
            if (prop->GetPropertyType () == FdoPropertyType_RasterProperty)
            {
                // we found that raster property
                rasterProp = static_cast<FdoRasterPropertyDefinition *> (FDO_SAFE_ADDREF (prop.p));
                bRaster = true;
                break;
            }
        }
    }

    // prune off the properties the caller hasn't provided in collection
    for (int j = properties->GetCount()-1; j >= 0; j--)
    {
        prop = properties->GetItem(j);
        bool bFound = false;

        for (int i = 0; i < mPropertiesToSelect->GetCount (); i++)
        {
            FdoPtr<FdoIdentifier> propValName = mPropertiesToSelect->GetItem (i);
            FdoString *propValNameString = propValName->GetName();

            if (0==wcscmp(prop->GetName(), propValNameString))
            {
                bFound = true; 
                break;
            }
        }

        // Remove this property, if not in propsToSelect
        if (!bFound)
        {
            // Remove from identity property collection, if necessary:
            if (prop->GetPropertyType() == FdoPropertyType_DataProperty)
            {
                FdoDataPropertyDefinition *dataProperty = static_cast<FdoDataPropertyDefinition*>(prop.p);
                if (ids->Contains(dataProperty))
                    ids->Remove(dataProperty);
            }

            // Remove from main property collection:
            properties->Remove(prop);
        }
    }

    // Add the computed properties
    for (FdoInt32 i = 0; i < mPropertiesToSelect->GetCount (); i++)
    {
        FdoPtr<FdoIdentifier> identifier = mPropertiesToSelect->GetItem (i);
        FdoComputedIdentifier* computedIdentifier = dynamic_cast<FdoComputedIdentifier*> (identifier.p);
        if (computedIdentifier)
        {
            FdoString* rasterPropName = computedIdentifier->GetName ();
            FdoPtr<FdoPropertyDefinition> prop = FdoCommonSchemaUtil::DeepCopyFdoPropertyDefinition (rasterProp);
            prop->SetName (rasterPropName);
            properties->Add (prop);
            ret->SetIsComputed (true);
        }
    }

    return FDO_SAFE_ADDREF (ret.p);
}

void FdoWcsSelectCommand::_setSpatialContextAssociation (FdoFeatureClass* featClass, FdoString* spatialContext)
{
    // firstly we search the raster property in the BaseProperties
    FdoPtr<FdoReadOnlyPropertyDefinitionCollection> baseProps = featClass->GetBaseProperties ();
    for (FdoInt32 i=0; i<baseProps->GetCount (); i++)
    {
        FdoPtr<FdoPropertyDefinition> baseProp = baseProps->GetItem (i);
        FdoRasterPropertyDefinition* rasterProp = dynamic_cast<FdoRasterPropertyDefinition *> (baseProp.p);
        if (rasterProp != NULL)
        {
            rasterProp->SetSpatialContextAssociation (spatialContext);
            return;
        }
    }

    FdoPtr<FdoPropertyDefinitionCollection> props = featClass->GetProperties ();
    for (FdoInt32 i=0; i<props->GetCount (); i++)
    {
        FdoPtr<FdoPropertyDefinition> prop = props->GetItem (i);
        FdoRasterPropertyDefinition* rasterProp = dynamic_cast<FdoRasterPropertyDefinition *> (prop.p);
        if (rasterProp != NULL)
        {
            rasterProp->SetSpatialContextAssociation (spatialContext);
            return;
        }
    }
}

FdoStringP FdoWcsSelectCommand::_getSpatialContextAssociation (FdoFeatureClass* featClass)
{
    FdoStringP spatialContext;	

    // firstly we search the raster property in the BaseProperties
    FdoPtr<FdoReadOnlyPropertyDefinitionCollection> baseProps = featClass->GetBaseProperties ();
    for (FdoInt32 i=0; i<baseProps->GetCount (); i++)
    {
        FdoPtr<FdoPropertyDefinition> baseProp = baseProps->GetItem (i);
        FdoRasterPropertyDefinition* rasterProp = dynamic_cast<FdoRasterPropertyDefinition *> (baseProp.p);
        if (rasterProp != NULL)
        {
            spatialContext = rasterProp->GetSpatialContextAssociation ();
            if (spatialContext == NULL)
            {
                throw FdoSchemaException::Create (NlsMsgGet(
                    FDOWCS_RASTERPROPERTY_HAS_NO_SPATIALCONTEXTASSOCIATION, 
                    "Raster property has no spatial context association."));
            }

            return spatialContext;
        }
    }

    FdoPtr<FdoPropertyDefinitionCollection> props = featClass->GetProperties ();
    for (FdoInt32 i=0; i<props->GetCount (); i++)
    {
        FdoPtr<FdoPropertyDefinition> prop = props->GetItem (i);
        FdoRasterPropertyDefinition* rasterProp = dynamic_cast<FdoRasterPropertyDefinition *> (prop.p);
        if (rasterProp != NULL)
        {
            spatialContext = rasterProp->GetSpatialContextAssociation ();
            if (spatialContext == NULL)
            {
                throw FdoSchemaException::Create (NlsMsgGet(
                    FDOWCS_RASTERPROPERTY_HAS_NO_SPATIALCONTEXTASSOCIATION, 
                    "Raster property has no spatial context association."));
            }

            return spatialContext;
        }
    }

    return spatialContext;	
}

// Analysis the properties to check whether the RESAMPLE or CLIP function is invoked.
void FdoWcsSelectCommand::_analysisIdentifier (FdoString* srsName, FdoOwsBoundingBox* bbox, FdoStringP& propertyName, FdoSize& height, FdoSize& width)
{
    bool bResample = false;
    bool bClip = false;

    FdoPtr<FdoIdentifierCollection> identifiers = GetPropertyNames ();

    for (FdoInt32 i=0; i<identifiers->GetCount (); i++)
    {
        FdoPtr<FdoIdentifier> identifier = identifiers->GetItem (i);
        FdoStringP propName = identifier->GetName ();
        FdoComputedIdentifier* computedIdentifier = dynamic_cast<FdoComputedIdentifier*> (identifier.p);

        if (computedIdentifier != NULL)
        {
            FdoPtr<FdoExpression> expr = computedIdentifier->GetExpression();
            FdoFunction* func = dynamic_cast<FdoFunction*> (expr.p);

            FdoWcsRect clippingBounds(0, 0, 0, 0);

            // CLIP() function can be nested
            while (true)
            {
                FdoString* funcName = func->GetName ();
                if (wcscmp (funcName, FdoWcsGlobals::ResampleFunctionName) == 0)
                    bResample = true;
                else if (wcscmp (funcName, FdoWcsGlobals::ClipFunctionName) == 0)
                    bClip = true;
                else
                    throw FdoCommandException::Create (NlsMsgGet (FDOWCS_ONLY_RESAMPLE_CLIP_FUNCTION_SUPPORTED, "Only RESAMPLE() and CLIP() function are supported."));

                FdoPtr<FdoExpressionCollection> args = func->GetArguments ();
                if (bResample && args->GetCount () != 7)
                    throw FdoCommandException::Create (NlsMsgGet (FDOWCS_RESAMPLE_FUNCTION_INCORRECTLY_USED, "RESAMPLE() function is incorrectly used."));
                if (bClip && args->GetCount() != 5)
                    throw FdoCommandException::Create (NlsMsgGet (FDOWCS_CLIP_FUNCTION_INCORRECTLY_USED, "CLIP() function is incorrectly used."));

                // Check the arguments types.
                FdoPtr<FdoExpression> arg0 = args->GetItem(0);

                FdoPtr<FdoExpression> arg = args->GetItem(1);
                FdoDataValue* minX = dynamic_cast<FdoDataValue*> (arg.p);
                arg = args->GetItem(2);
                FdoDataValue* minY = dynamic_cast<FdoDataValue*> (arg.p);
                arg = args->GetItem(3);
                FdoDataValue* maxX = dynamic_cast<FdoDataValue*> (arg.p);
                arg = args->GetItem(4);
                FdoDataValue* maxY = dynamic_cast<FdoDataValue*> (arg.p);

                FdoDataValue* heightArg = NULL;
                FdoDataValue* widthArg = NULL;

                if (bResample)
                {
                    arg = args->GetItem(5);
                    heightArg = dynamic_cast<FdoDataValue*> (arg.p);
                    arg = args->GetItem(6);
                    widthArg = dynamic_cast<FdoDataValue*> (arg.p);
                    if (minX == NULL || minY == NULL || maxX == NULL || maxY == NULL ||
                        heightArg == NULL || widthArg == NULL ||
                        (minX->GetDataType() != FdoDataType_Double && 
                        minX->GetDataType() != FdoDataType_Int16 &&
                        minX->GetDataType() != FdoDataType_Int32 && 
                        minX->GetDataType() != FdoDataType_Int64) ||
                        (minY->GetDataType() != FdoDataType_Double && 
                        minY->GetDataType() != FdoDataType_Int16 &&
                        minY->GetDataType() != FdoDataType_Int32 && 
                        minY->GetDataType() != FdoDataType_Int64)  ||
                        (maxX->GetDataType() != FdoDataType_Double && 
                        maxX->GetDataType() != FdoDataType_Int16 &&
                        maxX->GetDataType() != FdoDataType_Int32 && 
                        maxX->GetDataType() != FdoDataType_Int64)  ||
                        (maxY->GetDataType() != FdoDataType_Double && 
                        maxY->GetDataType() != FdoDataType_Int16 &&
                        maxY->GetDataType() != FdoDataType_Int32 && 
                        maxY->GetDataType() != FdoDataType_Int64) ||
                        (heightArg->GetDataType() != FdoDataType_Double && 
                        heightArg->GetDataType() != FdoDataType_Int16 &&
                        heightArg->GetDataType() != FdoDataType_Int32 && 
                        heightArg->GetDataType() != FdoDataType_Int64) ||
                        (widthArg->GetDataType() != FdoDataType_Double && 
                        widthArg->GetDataType() != FdoDataType_Int16 &&
                        widthArg->GetDataType() != FdoDataType_Int32 && 
                        widthArg->GetDataType() != FdoDataType_Int64) )
                        throw FdoCommandException::Create(NlsMsgGet(FDOWCS_RESAMPLE_FUNCTION_INCORRECTLY_USED, "RESAMPLE() function is incorrectly used."));
                }
                else if (bClip)
                {
                    if (minX == NULL || minY == NULL || maxX == NULL || maxY == NULL ||					
                        (minX->GetDataType() != FdoDataType_Double && 
                        minX->GetDataType() != FdoDataType_Int16 &&
                        minX->GetDataType() != FdoDataType_Int32 && 
                        minX->GetDataType() != FdoDataType_Int64) ||
                        (minY->GetDataType() != FdoDataType_Double && 
                        minY->GetDataType() != FdoDataType_Int16 &&
                        minY->GetDataType() != FdoDataType_Int32 && 
                        minY->GetDataType() != FdoDataType_Int64)  ||
                        (maxX->GetDataType() != FdoDataType_Double && 
                        maxX->GetDataType() != FdoDataType_Int16 &&
                        maxX->GetDataType() != FdoDataType_Int32 && 
                        maxX->GetDataType() != FdoDataType_Int64)  ||
                        (maxY->GetDataType() != FdoDataType_Double && 
                        maxY->GetDataType() != FdoDataType_Int16 &&
                        maxY->GetDataType() != FdoDataType_Int32 && 
                        maxY->GetDataType() != FdoDataType_Int64) )
                        throw FdoCommandException::Create(NlsMsgGet(FDOWCS_CLIP_FUNCTION_INCORRECTLY_USED, "CLIP() function is incorrectly used."));
                }

                FdoWcsRect bounds(atof ((const char*)FdoStringP(minX->ToString ())),
                    atof ((const char*)FdoStringP(minY->ToString ())),
                    atof ((const char*)FdoStringP(maxX->ToString ())),
                    atof ((const char*)FdoStringP(maxY->ToString ())));

                if (clippingBounds.IsValid())
                    clippingBounds = clippingBounds.Intersect (bounds);
                else
                    clippingBounds = bounds;

                if (bResample)
                {
                    height = atoi ((const char*)FdoStringP(heightArg->ToString ()));
                    width = atoi ((const char*)FdoStringP(widthArg->ToString ()));
                }
                else
                {
                    height = width = 0;
                }

                // try to find the next nested function
                // first try function
                func = dynamic_cast<FdoFunction *> (arg0.p);
                if (func == NULL)
                {
                    // then try computed property
                    FdoComputedIdentifier* computedIdentifier = dynamic_cast<FdoComputedIdentifier *> (arg0.p);
                    if (computedIdentifier != NULL)
                    {
                        FdoPtr<FdoExpression> expr = computedIdentifier->GetExpression();
                        func = dynamic_cast<FdoFunction *> (expr.p);
                    }
                }

                // Now arg0 must be an identifier, otherwise there must be somthing wrong with user's input
                if (func == NULL)
                {
                    FdoIdentifier* id = dynamic_cast<FdoIdentifier *> (arg0.p);
                    if (id == NULL)
                    {
                        if (bResample)
                            throw FdoCommandException::Create(NlsMsgGet(FDOWCS_RESAMPLE_FUNCTION_INCORRECTLY_USED, "RESAMPLE() function is incorrectly used."));
                        else if (bClip)
                            throw FdoCommandException::Create(NlsMsgGet(FDOWCS_CLIP_FUNCTION_INCORRECTLY_USED, "CLIP() function is incorrectly used."));
                    }
                    propertyName = id->GetText ();
                    break;
                }
            }

            bbox->SetMinX (clippingBounds.m_minX);
            bbox->SetMinY (clippingBounds.m_minY);
            bbox->SetMaxX (clippingBounds.m_maxX);
            bbox->SetMaxY (clippingBounds.m_maxY);

            // since we've got the computed property, stop the loop here
            break;
        }
    }

    if (!bClip && !bResample)
    {
        height = DEFAULTRESOLUTIONX;
        width = DEFAULTRESOLUTIONY;
        AdjustResolutionWithExtent(width, height, bbox->GetMinX(), bbox->GetMinY(), bbox->GetMaxX(), bbox->GetMaxY());
        if (width > 2048)
            width = 2048;
    }
}

// Get the default image height which is specified by the user in the connection string.
// if it's not set, use 600 pixels arbitarily as default height value.
FdoSize FdoWcsSelectCommand::_getDefaultImageHeight ()
{
    FdoPtr<FdoIConnectionInfo> info = mConnection->GetConnectionInfo ();
    FdoPtr<FdoIConnectionPropertyDictionary> dictionary = info->GetConnectionProperties ();
    FdoStringP defaultHeight = dictionary->GetProperty (FdoWcsGlobals::ConnectionPropertyDefaultImageHeight);

    return (defaultHeight == L"") ? 600 : (defaultHeight.ToLong ());
}

FdoString* FdoWcsSelectCommand::_getOriginalCoverageIdentifier (FdoString* mangledCoverageIdentifier)
{
    FdoPtr<FdoDictionary> nameMappings = mConnection->GetNamedCoverageMappings ();
    if (nameMappings != NULL)
    {
        // 1. the layer name is mangled
        FdoPtr<FdoDictionaryElement> nameMapping = nameMappings->FindItem (mangledCoverageIdentifier);
        if (nameMapping != NULL)
        {
            return nameMapping->GetValue ();
        }

        // 2. the input layer name may be the original one, so we should also verify it.
        FdoInt32 cnt = nameMappings->GetCount ();
        for (int i=0; i<cnt; i++)
        {
            FdoPtr<FdoDictionaryElement> nameMapping = nameMappings->GetItem (i);
            FdoStringP originalName = nameMapping->GetValue ();
            if (wcscmp (originalName, mangledCoverageIdentifier) == 0)
                return mangledCoverageIdentifier;
        }
    }

    // if not found, just return empty string.
    return L"";
}

void FdoWcsSelectCommand::AdjustResolutionWithExtent(FdoSize& resolutionX, FdoSize& resolutionY, double dMinX, double dMinY, double dMaxX, double dMaxY)
{
    double deltaExtentsX = fabs(dMaxX - dMinX);
    double deltaExtentsY = fabs(dMaxY - dMinY);

    // Adjust resolution to the next power of 2
    FdoSize maxresolution = (((resolutionX) > (resolutionY)) ? (resolutionX) : (resolutionY));
    FdoSize power2resolution;
    for (power2resolution=1; power2resolution<MAXPOWER2RESOLUTION && power2resolution<maxresolution; power2resolution<<=1)
        ;

    if (deltaExtentsX > deltaExtentsY)
    {
        resolutionX = power2resolution;
        resolutionY = static_cast<int>(resolutionX * deltaExtentsY / deltaExtentsX);
    }
    else
    {
        resolutionY = power2resolution;
        resolutionX = static_cast<int>(resolutionY * deltaExtentsX / deltaExtentsY);
    }
}
